﻿using AutoMapper;
using FreeSql;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using XUCore.Ddd.Domain;
using XUCore.Extensions;
using XUCore.NetCore.DynamicWebApi;
using XUCore.NetCore.FreeSql.Entity;

namespace XUCore.NetCore.FreeSql.Curd
{
    /// <summary>
    /// CURD服务
    /// </summary>
    /// <typeparam name="TKey">主键类型</typeparam>
    /// <typeparam name="TEntity">数据库实体</typeparam>
    /// <typeparam name="TDto">返回实体</typeparam>
    public abstract class CurdService<TKey, TEntity, TDto> : ICurdService<TKey, TEntity, TDto>
            where TEntity : EntityFull<TKey>, new()
            where TDto : class, new()
    {
        /// <summary>
        /// 创建事件
        /// </summary>
        public Action<TEntity> CreatedAction { get; set; }
        /// <summary>
        /// 修改事件
        /// </summary>
        public Action<TEntity> UpdatedAction { get; set; }
        /// <summary>
        /// 删除事件
        /// </summary>
        public Action<IList<TKey>> DeletedAction { get; set; }
        /// <summary>
        /// 软删除事件
        /// </summary>
        public Action<IList<TKey>> SoftDeletedAction { get; set; }
        /// <summary>
        /// 用户信息
        /// </summary>
        public readonly IUser user;
        /// <summary>
        /// freesql
        /// </summary>
        public readonly IFreeSql freeSql;
        /// <summary>
        /// 仓储
        /// </summary>
        public readonly IBaseRepository<TEntity> repo;
        /// <summary>
        /// AutoMapper
        /// </summary>
        public readonly IMapper mapper;
        /// <summary>
        /// CURD服务
        /// </summary>
        /// <param name="freeSql"></param>
        /// <param name="mapper"></param>
        /// <param name="user"></param>
        public CurdService(IFreeSql freeSql, IMapper mapper, IUser user)
        {
            this.freeSql = freeSql;
            this.mapper = mapper;
            this.repo = freeSql.GetRepository<TEntity>();
            this.user = user;
        }
        /// <summary>
        /// 设置更新的列<para></para>
        ///     注意：标记 [Column(CanUpdate = false)] 的属性不会被更新
        /// </summary>
        /// <param name="id"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpPut]
        [DynamicMethod(IsVisible = false)]
        public virtual async Task<int> UpdateFieldAsync([FromQuery] TKey id, [FromQuery] string field, [FromQuery] string value, CancellationToken cancellationToken = default)
            => await BatchUpdateFieldAsync(new TKey[] { id }, field, value, cancellationToken);
        /// <summary>
        /// 批量设置更新的列<para></para>
        ///     注意：标记 [Column(CanUpdate = false)] 的属性不会被更新<para></para>
        /// </summary>
        /// <param name="ids"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpPut]
        [DynamicMethod(IsVisible = false)]
        public virtual async Task<int> BatchUpdateFieldAsync([FromQuery] TKey[] ids, [FromQuery] string field, [FromQuery] string value, CancellationToken cancellationToken = default)
        {
            return await repo.UpdateDiy.WhereDynamic(ids)
                .SetDto(new Dictionary<string, object>
                {
                    [field] = value,
                    [nameof(EntityFull<TKey>.ModifiedAtUserId)] = user?.Id.ToLong(),
                    [nameof(EntityFull<TKey>.ModifiedAtUserName)] = user?.Name,
                    [nameof(EntityFull<TKey>.ModifiedAt)] = DateTime.Now,
                })
                .ExecuteAffrowsAsync(cancellationToken);
        }
        /// <summary>
        /// 物理删除数据
        /// </summary>
        /// <param name="id"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpDelete]
        public virtual async Task<int> DeleteAsync([FromQuery] TKey id, CancellationToken cancellationToken = default)
            => await BatchDeleteAsync(new TKey[] { id }, cancellationToken);
        /// <summary>
        /// 批量物理删除数据
        /// </summary>
        /// <param name="ids"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpDelete]
        [DynamicMethod(IsVisible = false)]
        public virtual async Task<int> BatchDeleteAsync([FromQuery] TKey[] ids, CancellationToken cancellationToken = default)
        {
            var res = await repo.Select.WhereDynamic(ids).ToDelete().ExecuteAffrowsAsync(cancellationToken);

            if (res > 0)
                DeletedAction?.Invoke(ids);
            return res;
        }
        /// <summary>
        /// 逻辑删除
        /// </summary>
        /// <param name="id"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpDelete]
        public virtual async Task<int> SoftDeleteAsync([FromQuery] TKey id, CancellationToken cancellationToken = default)
            => await BatchSoftDeleteAsync(new TKey[] { id }, cancellationToken);
        /// <summary>
        /// 批量逻辑删除
        /// </summary>
        /// <param name="ids"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpDelete]
        [DynamicMethod(IsVisible = false)]
        public virtual async Task<int> BatchSoftDeleteAsync([FromQuery] TKey[] ids, CancellationToken cancellationToken = default)
        {
            var res = await repo.UpdateDiy.WhereDynamic(ids)
                .SetDto(new TEntity
                {
                    IsDeleted = true,
                    ModifiedAtUserId = user?.Id.ToLong(),
                    ModifiedAtUserName = user?.Name,
                    ModifiedAt = DateTime.Now
                })
                .ExecuteAffrowsAsync(cancellationToken);

            if (res > 0)
                SoftDeletedAction?.Invoke(ids);
            return res;
        }
        /// <summary>
        /// 根据id获取一条记录
        /// </summary>
        /// <param name="id"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        [HttpGet("{id}")]
        public virtual async Task<TDto> GetAsync(TKey id, CancellationToken cancellationToken = default)
        {
            var res = await repo.Select.WhereDynamic(id).ToOneAsync(cancellationToken);

            return mapper.Map<TEntity, TDto>(res);
        }
    }
}
